package com.acgist.polaris.entity;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.acgist.polaris.Config;
import com.acgist.polaris.Config.Type;
import com.acgist.polaris.Config.Version;
import com.acgist.polaris.Tools;

/**
 * <p>数据流信息</p>
 * 
 * +--------------------------+
 * |         压缩描述         |
 * +--------------------------+
 * | message.length | message |
 * +--------------------------+
 * | int            | byte[]  |
 * +--------------------------+
 * 
 * @author acgist
 */
public class StreamEntity implements Serializable {
	
	private static final long serialVersionUID = 1L;

	private static final Logger LOGGER = LoggerFactory.getLogger(StreamEntity.class);
	
	/**
	 * <p>描述数据长度：{@value}</p>
	 */
	protected static final int DESCRIPTION_LENGTH = 4;
	
	/**
	 * <p>算法名称</p>
	 */
	protected final String algo;
	/**
	 * <p>算法版本</p>
	 */
	protected final Config.Version version;
	/**
	 * <p>压缩数据类型</p>
	 */
	protected final Config.Type type;
	/**
	 * <p>数据数据原始大小</p>
	 */
	protected Long size;
	
	/**
	 * @see #newStreamEntity()
	 */
	private StreamEntity() {
		this(Config.Type.STREAM);
	}

	/**
	 * @param type 数据压缩类型
	 */
	protected StreamEntity(Config.Type type) {
		this.type = type;
		this.algo = Config.getInstance().getAlgo();
		this.version = Config.Version.newest();
	}
	
	/**
	 * <p>获取数据流实体</p>
	 * 
	 * @return 数据流实体
	 */
	public static final StreamEntity newStreamEntity() {
		return new StreamEntity();
	}

	/**
	 * <p>添加数据</p>
	 */
	public void putPayload() {
		// TODO：添加数据
	}
	
	/**
	 * <p>获取数据描述信息</p>
	 * <p>数据描述信息：算法名称、算法版本、压缩数据类型、原始数据原始大小</p>
	 * 
	 * @return 数据描述
	 */
	public byte[] buildDescription() {
		final byte[] algoBytes = this.algo.getBytes();
		final byte[] versionBytes = this.version.version().getBytes();
		final byte[] typeBytes = new byte[] {this.type.value()};
		final byte[] sizeBytes = Tools.longToBytes(this.size);
		final int length =
			DESCRIPTION_LENGTH + algoBytes.length +
			DESCRIPTION_LENGTH + versionBytes.length +
			DESCRIPTION_LENGTH + typeBytes.length +
			DESCRIPTION_LENGTH + sizeBytes.length;
		final ByteBuffer buffer = ByteBuffer.allocate(length);
		buffer
			.putInt(algoBytes.length).put(algoBytes)
			.putInt(versionBytes.length).put(versionBytes)
			.putInt(typeBytes.length).put(typeBytes)
			.putInt(sizeBytes.length).put(sizeBytes);
		return buffer.array();
	}
	
	/**
	 * <p>读取数据描述信息</p>
	 * 
	 * @param input 输入数据流
	 * 
	 * @return 数据流信息
	 * 
	 * @throws IOException IO异常
	 */
	public StreamEntity buildDescription(InputStream input) throws IOException {
		final String algo = new String(this.readDescriptionInfo(input));
		if(!this.algo.equals(algo)) {
			LOGGER.debug("压缩和解压算法不一致：{}-{}", this.algo, algo);
		}
		final Version version = Config.Version.of(new String(this.readDescriptionInfo(input)));
		if(this.version != version) {
			LOGGER.debug("压缩和解压版本不一致：{}-{}", this.version, version);
		}
		final byte[] typeBytes = this.readDescriptionInfo(input);
		final Type type = Type.of(typeBytes[0]);
		if(this.type != type) {
			LOGGER.debug("压缩和解压类型不一致：{}-{}", this.type, type);
		}
		this.size = Tools.bytesToLong(this.readDescriptionInfo(input));
		return this;
	}
	
	/**
	 * <p>读取数据描述信息</p>
	 * 
	 * @param input 输入数据流
	 * 
	 * @return 描述信息
	 * 
	 * @throws IOException IO异常
	 */
	protected byte[] readDescriptionInfo(InputStream input) throws IOException {
		final byte[] bytes = new byte[DESCRIPTION_LENGTH];
		input.read(bytes);
		final int length = Tools.bytesToInt(bytes);
		final byte[] values = new byte[length];
		input.read(values);
		return values;
	}
	
	public Config.Type getType() {
		return type;
	}

	public String getAlgo() {
		return algo;
	}

	public Config.Version getVersion() {
		return version;
	}

	public Long getSize() {
		return size;
	}

	public void setSize(Long size) {
		this.size = size;
	}

}
